package cz.muni.fi.xvano1.trinity.beans;

import cz.muni.fi.xvano1.trinity.core.Core;
import cz.muni.fi.xvano1.trinity.supplementary.ActionManagerBeanContext;
import cz.muni.fi.xvano1.trinity.supplementary.CalendarHandler;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sourceforge.stripes.action.*;

/**
 * Class implements methods of displaying reservation schedule in the system.
 * Due to the framework nature, getter and setter methods for parameters of
 * request have been implemented
 *
 * @author Jan Vano
 */
public class CalendarViewActionBean implements ActionBean {

    private ActionManagerBeanContext context;
    private String viewType;
    private int year;
    private String yearStr;
    private int month = 1;
    private String monthStr;
    private int week = 1;
    private String weekStr;
    private String day;

    /**
     * Interface method from ActionBean.
     */
    @Override
    public void setContext(ActionBeanContext context) {
        this.context = (ActionManagerBeanContext) context;
    }

    public String getViewType() {
        return viewType;
    }

    public void setViewType(String viewType) {
        this.viewType = viewType;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    public int getWeek() {
        return week;
    }

    public void setWeek(int week) {
        this.week = week;
    }

    public String getDay() {
        return day;
    }

    public void setDay(String day) {
        this.day = day;
    }

    public String getYearStr() {
        return yearStr;
    }

    public void setYearStr(String yearStr) {
        this.yearStr = yearStr;
    }

    public String getMonthStr() {
        return monthStr;
    }

    public void setMonthStr(String monthStr) {
        this.monthStr = monthStr;
    }

    public String getWeekStr() {
        return weekStr;
    }

    public void setWeekStr(String weekStr) {
        this.weekStr = weekStr;
    }

    /**
     * Interface method from ActionBean, using a co-variant return type.
     */
    @Override
    public ActionManagerBeanContext getContext() {
        return this.context;
    }

    /**
     * Method return number of weeks for a specific year. Czech locale and time
     * is used.
     *
     * @param year Year
     * @return Count of its weeks
     */
    public List<Integer> getWeeks() {
        java.util.Calendar cal = java.util.Calendar.getInstance(new SimpleTimeZone(0, "Europe/Prague"), new Locale("cs"));
        cal.set(java.util.Calendar.YEAR, year);
        int max = cal.getActualMaximum(java.util.Calendar.WEEK_OF_YEAR);
        List<Integer> result = new ArrayList<Integer>();
        for (int i = 1; i <= max; i++) {
            result.add(i);
        }
        return result;
    }

    /**
     * Method return number of days for a specific year and month. Czech locale
     * and time is used.
     *
     * @return Count of its weeks
     */
    public List<Integer> getDays() {
        java.util.Calendar cal = java.util.Calendar.getInstance(new SimpleTimeZone(0, "Europe/Prague"), new Locale("cs"));
        cal.set(java.util.Calendar.YEAR, year);
        cal.set(java.util.Calendar.MONTH, month);
        int max = cal.getActualMaximum(java.util.Calendar.DATE);
        List<Integer> result = new ArrayList<Integer>();
        for (int i = 1; i <= max; i++) {
            result.add(i);
        }
        return result;
    }

    /**
     * Method for computing aggregated calendar structure
     *
     * @param toolboxes Set of toolboxes for which the structure should be
     * computed
     * @return Computed structure that shows maximum of license use throughout a
     * set of toolboxes
     */
    public Map<String, Integer> getCalendarStructureForToolboxes(Set<String> toolboxes) {
        if (toolboxes == null) {
            return null;
        }
        Map<String, Integer> cal = getCalendar();
        if (cal == null) {
            return null;
        }
        Map<String, Integer> newCal = new HashMap<String, Integer>();
        Set<String> timeKeys = cal.keySet();
        Set<String> compatibleKeys = new HashSet<String>();
        for (String key : timeKeys) {
            for (String toolbox : toolboxes) {
                if (toolbox.equals(key.split(";")[1])) {    //TOP FIX
                    compatibleKeys.add(key);
                    break;
                }
            }
        }
        for (String key : compatibleKeys) {
            String time = key.split(";")[0];
            Integer base = newCal.get(time);    //TOP FIX
            if (base == null) {
                base = 0;
            }
            Integer addition = cal.get(key);
            if (addition == null) {
                addition = 0;
            }
            newCal.put(time, (base > addition ? base : addition));
        }
        return newCal;
    }

    /**
     * Method for storing calendar structure into memory.
     */
    public void synchronizeCalendar() {
        synchronized (getContext().getServletContext().getAttribute("userAccessSync")) {
            BufferedReader r = null;
            try {
                r = new BufferedReader(new FileReader(Core.calendar));
            } catch (FileNotFoundException ex) {
                    Logger.getLogger(CalendarViewActionBean.class.getName()).log(Level.SEVERE, null, ex);
                }
            getContext().getServletContext().setAttribute("calendar", CalendarHandler.getCalendarStructure(r));
        }
    }

    /**
     * Method for loading calendar structure from internal memory.
     *
     * @return Calendar structure
     */
    @SuppressWarnings("unchecked")
    public Map<String, Integer> getCalendar() {
        Object o = getContext().getServletContext().getAttribute("calendar");
        Map<String, Integer> result = null;
        if (o instanceof Map<?, ?>) {
            result = (Map<String, Integer>) o;
        }
        return result;
    }

    /**
     * Method forwards user to a calendar view option page
     *
     * @return Resolution object to framework
     */
    public Resolution calendarView() {
        getContext().getRequest().setAttribute("action", "calendarOptions");
        return new ForwardResolution("/Index");
    }

    /**
     * Method forwards user to appropriate calendar view chosen by the user
     *
     * @return Resolution object to framework
     */
    @DefaultHandler
    public Resolution selectViewType() {
        if ("Yearly".equals(viewType)) {
            getContext().getRequest().setAttribute("action", "yearlyView");
        }
        if ("Monthly".equals(viewType)) {
            getContext().getRequest().setAttribute("action", "monthlyView");
        }
        if ("Weekly".equals(viewType)) {
            getContext().getRequest().setAttribute("action", "weeklyView");
        }
        if ("Daily".equals(viewType)) {
            getContext().getRequest().setAttribute("action", "dailyView");
        }
        return new ForwardResolution("/Index");
    }

    /**
     * Method select year in yearly calendar view
     *
     * @return Resolution object to framework
     */
    public Resolution selectYearOfYearly() {
        getContext().getRequest().setAttribute("year", year);
        getContext().getRequest().setAttribute("action", "yearlyView");
        return new ForwardResolution("/Index");
    }

    /**
     * Method select year in monthly view
     *
     * @return Resolution object to framework
     */
    public Resolution selectYearOfMonthly() {
        getContext().getRequest().setAttribute("year", year);
        getContext().getRequest().setAttribute("action", "monthlyView");
        return new ForwardResolution("/Index");
    }

    /**
     * Method select year in weekly view
     *
     * @return Resolution object to framework
     */
    public Resolution selectYearOfWeekly() {
        getContext().getRequest().setAttribute("year", year);
        getContext().getRequest().setAttribute("weeks", getWeeks());
        getContext().getRequest().setAttribute("action", "weeklyView");
        return new ForwardResolution("/Index");
    }

    /**
     * Method select year in daily view
     *
     * @return Resolution object to framework
     */
    public Resolution selectYearOfDaily() {
        getContext().getRequest().setAttribute("year", year);
        getContext().getRequest().setAttribute("action", "dailyView");
        return new ForwardResolution("/Index");
    }

    /**
     * Method select month in monthly view
     *
     * @return Resolution object to framework
     */
    public Resolution selectMonthOfMonthly() {
        getContext().getRequest().setAttribute("year", year);
        getContext().getRequest().setAttribute("month", month);
        getContext().getRequest().setAttribute("action", "monthlyView");
        return new ForwardResolution("/Index");
    }

    /**
     * Method select month in daily view
     *
     * @return Resolution object to framework
     */
    public Resolution selectMonthOfDaily() {
        getContext().getRequest().setAttribute("year", year);
        getContext().getRequest().setAttribute("month", month);
        getContext().getRequest().setAttribute("days", getDays());
        getContext().getRequest().setAttribute("action", "dailyView");
        return new ForwardResolution("/Index");
    }

    /**
     * Method select week in weekly view
     *
     * @return Resolution object to framework
     */
    public Resolution selectWeekOfWeekly() {
        getContext().getRequest().setAttribute("year", year);
        getContext().getRequest().setAttribute("week", week);
        getContext().getRequest().setAttribute("action", "weeklyView");
        return new ForwardResolution("/Index");
    }

    /**
     * Method select week in weekly view
     *
     * @return Resolution object to framework
     */
    public Resolution selectDayOfDaily() {
        getContext().getRequest().setAttribute("year", year);
        getContext().getRequest().setAttribute("month", month);
        getContext().getRequest().setAttribute("day", day);
        getContext().getRequest().setAttribute("action", "dailyView");
        return new ForwardResolution("/Index");
    }

    /**
     * Method view yearly calendar based on user input of year.
     *
     * @return Resolution object to framework
     */
    public Resolution viewYearlyCalendar() {
        List<String> allowedKeys = (List<String>) getContext().getServletContext().getAttribute(("allowedKeys"));
        Set<String> newKeys = new HashSet<String>();
        List<String> newKeysList = new ArrayList<String>();
        if (allowedKeys != null) {
            newKeys = new HashSet<String>(allowedKeys);
        } else {
            System.out.println("Null pointer allowedKeys at viewYearlyCalendar()");
        }
        for (String key : allowedKeys) {
            if (getContext().getRequest().getParameter(key) == null) {
                newKeys.remove(key);
            } else {
                newKeysList.add(key);
            }
        }
        synchronizeCalendar();
        Map<String, Integer> customCalendar = getCalendarStructureForToolboxes(newKeys);
//        String toolbox = "MATLAB";
        Map<Integer, Map<Integer, List<Integer>>> yearArray = CalendarHandler.getYearArray(year);
        Map<Integer, String> months = CalendarHandler.getMonthNames();

        getContext().getRequest().setAttribute("view", customCalendar);
        getContext().getRequest().setAttribute("year", year);
        getContext().getRequest().setAttribute("map", yearArray);
        getContext().getRequest().setAttribute("months", months);
        getContext().getRequest().setAttribute("newKeysList", newKeysList);
        getContext().getRequest().setAttribute("action", "calendarYearView");
        return new ForwardResolution("/Index");
    }

    /**
     * Method view monthly calendar view based on user input of year and month.
     *
     * @return Resolution object to framework
     */
    public Resolution viewMonthlyCalendar() {
        if (getMonthStr() != null) {
            try {
                setMonth(Integer.parseInt(getMonthStr()));
            } catch (NumberFormatException ex) {
            }
        }
        List<String> allowedKeys = (List<String>) getContext().getServletContext().getAttribute(("allowedKeys"));
        Set<String> newKeys = new HashSet<String>();
        List<String> newKeysList = new ArrayList<String>();
        if (allowedKeys != null) {
            newKeys = new HashSet<String>(allowedKeys);
        } else {
            System.out.println("Null pointer allowedKeys at viewMonthlyCalendar()");
        }
        for (String key : allowedKeys) {
            if (getContext().getRequest().getParameter(key) == null) {
                newKeys.remove(key);
            } else {
                newKeysList.add(key);
            }
        }
        synchronizeCalendar();
        Map<String, Integer> customCalendar = getCalendarStructureForToolboxes(newKeys);
        Map<String, Integer> calendar = getCalendar();
//        String toolbox = "MATLAB";
        Map<Integer, String> months = CalendarHandler.getMonthNames();
        int maxDate = CalendarHandler.getLastDayInMonth(year, month);

        getContext().getRequest().setAttribute("view", customCalendar);
        getContext().getRequest().setAttribute("year", year);
        getContext().getRequest().setAttribute("month", month);
        getContext().getRequest().setAttribute("calendar", calendar);
        getContext().getRequest().setAttribute("monthName", months.get(month));
        getContext().getRequest().setAttribute("newKeysList", newKeysList);
        getContext().getRequest().setAttribute("maxDate", maxDate);
        getContext().getRequest().setAttribute("tableColls", (newKeysList.size() + 2));
        getContext().getRequest().setAttribute("action", "calendarMonthView");
        return new ForwardResolution("/Index");
    }

    /**
     * Method view weekly calendar view based on user input of year and month.
     *
     * @return Resolution object to framework
     */
    public Resolution viewWeeklyCalendar() {
        if (getYearStr() != null) {
            try {
                setYear(Integer.parseInt(getYearStr()));
            } catch (NumberFormatException ex) {
            }
        }
        if (getWeekStr() != null) {
            try {
                setWeek(Integer.parseInt(getWeekStr()));
            } catch (NumberFormatException ex) {
            }
        }
        List<String> allowedKeys = (List<String>) getContext().getServletContext().getAttribute(("allowedKeys"));
        Set<String> newKeys = new HashSet<String>();
        List<String> newKeysList = new ArrayList<String>();
        if (allowedKeys != null) {
            newKeys = new HashSet<String>(allowedKeys);
        } else {
            System.out.println("Null pointer allowedKeys at viewWeeklyCalendar()");
        }
        for (String key : allowedKeys) {
            if (getContext().getRequest().getParameter(key) == null) {
                newKeys.remove(key);
            } else {
                newKeysList.add(key);
            }
        }
        synchronizeCalendar();
        Map<String, Integer> customCalendar = getCalendarStructureForToolboxes(newKeys);
        Map<String, Integer> calendar = getCalendar();

        getContext().getRequest().setAttribute("view", customCalendar);
        getContext().getRequest().setAttribute("year", year);
        getContext().getRequest().setAttribute("week", week);
        getContext().getRequest().setAttribute("calendar", calendar);
        getContext().getRequest().setAttribute("newKeysList", newKeysList);
        getContext().getRequest().setAttribute("tableColls", (newKeysList.size() + 2));
        getContext().getRequest().setAttribute("action", "calendarWeekView");
        getContext().getRequest().setAttribute("days", CalendarHandler.getDays(year, week));
        return new ForwardResolution("/Index");
    }

    /**
     * Method view daily calendar view based on user input of year, month and
     * day.
     *
     * @return Resolution object to framework
     */
    public Resolution viewDailyCalendar() {
        if (getMonthStr() != null) {
            try {
                setMonth(Integer.parseInt(getMonthStr()));
            } catch (NumberFormatException ex) {
            }
        }
        List<String> allowedKeys = (List<String>) getContext().getServletContext().getAttribute(("allowedKeys"));
        Set<String> newKeys = new HashSet<String>();
        List<String> newKeysList = new ArrayList<String>();
        if (allowedKeys != null) {
            newKeys = new HashSet<String>(allowedKeys);
        } else {
            System.out.println("Null pointer allowedKeys at viewDailyCalendar()");
        }
        for (String key : allowedKeys) {
            if (getContext().getRequest().getParameter(key) == null) {
                newKeys.remove(key);
            } else {
                newKeysList.add(key);
            }
        }
        synchronizeCalendar();
        Map<String, Integer> customCalendar = getCalendarStructureForToolboxes(newKeys);
        Map<String, Integer> calendar = getCalendar();

        getContext().getRequest().setAttribute("view", customCalendar);
        getContext().getRequest().setAttribute("year", year);
        getContext().getRequest().setAttribute("month", month);
        getContext().getRequest().setAttribute("day", day);
        getContext().getRequest().setAttribute("calendar", calendar);
        getContext().getRequest().setAttribute("timeStamps", CalendarHandler.getStamps());
        getContext().getRequest().setAttribute("newKeysList", newKeysList);
        getContext().getRequest().setAttribute("tableColls", (newKeysList.size() + 2));
        getContext().getRequest().setAttribute("action", "calendarDayView");
        return new ForwardResolution("/Index");
    }
}
